# -*- coding: utf-8 -*-
"""
纯内存无依赖的领域服务，所有外部依赖通过构造器注入。
"""
from __future__ import annotations

import json
import uuid
from typing import Optional, Dict, Any

from common.database import DatabaseManager
from common.logger import create_logger
from mqtt.consumer.event_models import QualityEvent
from mqtt.consumer.repository.alarm_repository import AlarmRepository
from mqtt.consumer.repository.work_order_repository import WorkOrderRepository
from .event import AlarmRaisedEvent, AlarmRecoveredEvent, WorkOrderClosedEvent
from .strategy import RecoveryStrategy, WorkOrderCloseStrategy

logger = create_logger("domain.service", "INFO")


class AlarmService:
	def __init__(
			self,
			db: DatabaseManager,
			recovery_strategy: RecoveryStrategy,
			close_strategy: WorkOrderCloseStrategy,
	):
		self.db = db
		self.recovery_strategy = recovery_strategy
		self.close_strategy = close_strategy
		# 初始化 Repository
		self.alarm_repo = AlarmRepository(db)
		self.work_order_repo = WorkOrderRepository(db)

	# ---------- 事务内：产生告警 ----------
	def raise_alarm(self, event: QualityEvent, alarm_type: str) -> AlarmRaisedEvent:
		logger.info(f"[DIAG-DomainService] 创建告警: {alarm_type}, {event.meter_code}")

		snap = {
			"voltage": str(event.voltage),
			"current": str(event.current),
			"watt": str(event.watt),
		}
		recover_token = str(uuid.uuid4())

		alarm_id = self.alarm_repo.insert_alarm(
			event.msg_id,
			event.meter_code,
			event.meter_type,
			alarm_type,
			json.dumps(snap, ensure_ascii=False),
			recover_token
		)

		logger.info("[Domain] 告警已生成 alarm_id=%s alarm_type=%s meter_code=%s", alarm_id, alarm_type, event.meter_code)

		# 查询告警详情，获取 rule_name
		alarm_info = self.alarm_repo.get_alarm_info_by_id(alarm_id)
		rule_name = alarm_info.get('rule_name', '') if alarm_info else ''

		return AlarmRaisedEvent(
			alarm_id=alarm_id,
			msg_id=event.msg_id,
			meter_code=event.meter_code,
			alarm_type=alarm_type,
			snapshot=snap,
			rule_name=rule_name,
			raised_at=None,  # 将由数据库自动设置
		)

	# ---------- 事务内：恢复检测 ----------
	def recover_if_needed(self, event: QualityEvent) -> Optional[AlarmRecoveredEvent]:
		"""
		检查并执行告警恢复逻辑（高并发优化版本）

		使用原子性查询消除竞争条件，确保在高并发场景下的稳定性。

		Args:
			event: 质量事件

		Returns:
			Optional[AlarmRecoveredEvent]: 恢复事件或None
		"""
		try:
			# 使用原子性查询，直接获取开放告警并加锁，避免并发竞争
			open_alarms = self.alarm_repo.find_open_alarms_with_lock(event.meter_code)

			if not open_alarms:
				return None

			open_types = [alarm['alarm_type'] for alarm in open_alarms]
			logger.info(f"[DIAG-Recovery] {event.meter_code} 开放告警: {open_types}")

			# 遍历已锁定的告警记录，检查恢复条件
			for alarm in open_alarms:
				alarm_type = alarm['alarm_type']

				# 检查恢复条件
				if not self.recovery_strategy.is_recovered(event, alarm_type):
					continue

				logger.info(f"[DIAG-Recovery] {event.meter_code} {alarm_type} 满足恢复条件")

				# 执行恢复操作
				recovered_event = self._execute_alarm_recovery(alarm, event)
				if recovered_event:
					return recovered_event

			return None

		except Exception as e:
			logger.error(f"[DIAG-Recovery] {event.meter_code} 恢复检查异常: {e}")
			raise

	def _execute_alarm_recovery(self, alarm: Dict[str, Any], event: QualityEvent) -> Optional[AlarmRecoveredEvent]:
		"""
		执行单个告警的恢复操作（内部方法）

		Args:
			alarm: 告警记录
			event: 质量事件

		Returns:
			Optional[AlarmRecoveredEvent]: 恢复事件或None
		"""
		alarm_id = alarm['id']
		alarm_type = alarm['alarm_type']
		meter_code = event.meter_code

		# 生成恢复令牌
		recover_token = str(uuid.uuid4())
		logger.info(f"[DIAG-Recovery] {meter_code} {alarm_type} 开始更新告警 alarm_id={alarm_id}")

		try:
			# 执行恢复更新
			rows_affected = self.alarm_repo.update_alarm_recovered(alarm_id, recover_token)
			logger.info(f"[DIAG-Recovery] {meter_code} {alarm_type} 更新结果: 受影响行数={rows_affected}")

			if rows_affected > 0:
				logger.info("[Domain] 告警已恢复 alarm_id=%s alarm_type=%s meter_code=%s", alarm_id, alarm_type, meter_code)

				# 查询告警详情，获取 rule_name
				alarm_info = self.alarm_repo.get_alarm_info_by_id(alarm_id)
				rule_name = alarm_info.get('rule_name', '') if alarm_info else ''

				return AlarmRecoveredEvent(
					alarm_id=alarm_id,
					msg_id=event.msg_id,
					meter_code=meter_code,
					alarm_type=alarm_type,
					rule_name=rule_name,
					recovered_at=None,  # 将由数据库自动设置
				)
			else:
				logger.warning(f"[DIAG-Recovery] {meter_code} {alarm_type} 更新告警失败，可能是并发冲突")
				return None

		except Exception as e:
			if "Duplicate entry" in str(e):
				return None
			else:
				logger.error(f"[DIAG-Recovery] {meter_code} {alarm_type} 恢复处理异常: {e}")
				raise

	# ---------- 事务内：关单 ----------
	def close_work_order_if_needed(self, alarm_id: int) -> Optional[WorkOrderClosedEvent]:
		if not self.close_strategy.should_close_on_recovered():
			return None

		# 查找需要关闭的工单和关联的告警信息
		wo_row = self.work_order_repo.find_open_work_order_by_alarm(alarm_id)
		if not wo_row:
			return None

		# 获取告警信息以获取meter_code和alarm_type
		alarm_info = self.alarm_repo.get_alarm_info_by_id(alarm_id)
		meter_code = alarm_info.get('meter_code', '') if alarm_info else ''
		alarm_type = alarm_info.get('alarm_type', 'UNKNOWN') if alarm_info else 'UNKNOWN'

		wo_id = wo_row["id"]
		# 自动关闭工单
		self.work_order_repo.auto_close_work_order(wo_id)
		# 记录流转日志
		self.work_order_repo.insert_work_order_log(wo_id, "告警恢复，系统自动关闭工单")
		logger.info("[Domain] 工单已关闭 wo_id=%s alarm_id=%s meter_code=%s alarm_type=%s", wo_id, alarm_id, meter_code, alarm_type)
		return WorkOrderClosedEvent(
			work_order_id=wo_id,
			alarm_id=alarm_id,
			meter_code=meter_code if meter_code else None,
			alarm_type=alarm_type if alarm_type and alarm_type != 'UNKNOWN' else None,
			closed_at=None,  # 将由数据库自动设置
		)
